package com.puzzlebazar.client.puzzleinfo;

import java.util.Date;

import com.puzzlebazar.client.puzzle.model.PuzzleModel;
import com.puzzlebazar.client.util.DateUtil;
import com.puzzlebazar.client.util.TextUtil;

public class PuzzleInfo extends NewPuzzleInfo {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5219482761359373781L;

	public enum Validity {
		UNKNOWN,
		SOLUTION_UNKNOWN,
		NO_SOLUTION,
		ONE_SOLUTION,    // Tentatively valid
		UNIQUE_SOLUTION, // Valid
		MULTIPLE_SOLUTIONS		
	}

	public enum Status {
		UNKNOWN,
		PRIVATE,
		PUBLISHED,
		IN_REVIEW,
		ACCEPTED,
		REVIEW_PUBLISHED
	}

	public final static int MAX_DIFFICULTY = 3;
	public final static int MAX_QUALITY = 3;

	private String key = null;
    private String authorUsername = "";
    private Status status = Status.UNKNOWN;
    private Validity validity = Validity.UNKNOWN;
	private Date dateCreated = null;
	private Date dateEdited = null;
	private Date datePublished = null;
	private float difficulty = 0;
	private float quality = 0;
	private PuzzleModel puzzleModel = null;
	private boolean modelInDatastore = false;
	private String tutorialHistoryKey = null;
	
	private String specificUserEmail = null;
	private String solvedByUserHistoryKey = null;
	private String startedByUserHistoryKey = null;

	/**
	 * @return The key for this puzzle info model
	 */
	public String getKey() {
		return key;
	}
	
	/**
	 * @return The author of this puzzle
	 */
	public String getAuthorUsername() {
		return authorUsername;
	}

	/**
	 * @return The creation date of this puzzle
	 */
	public Date getDateCreated() {
		return dateCreated;
	}

	/**
	 * @return The last edition date of this puzzle
	 */
	public Date getDateEdited() {
		return dateEdited;
	}

	/**
	 * @return The publication date of this puzzle
	 */
	public Date getDatePublished() {
		return datePublished;
	}

	/**
	 * @return The publication status of this puzzle
	 */
	public Status getStatus() {
		return status;
	}
	
	/**
	 * @return The validity status of this puzzle
	 */
	public Validity getValidity() {
		return validity;
	}
	
	/**
	 * @return True if the puzzle is valid (or tentatively valid)
	 */
	public boolean isValid() {
		return validity == Validity.ONE_SOLUTION || validity == Validity.UNIQUE_SOLUTION;
	}
	
	/**
	 * @return The difficulty of this puzzle
	 */
	public float getDifficulty() {
		return difficulty;
	}

	/**
	 * @return The quality of this puzzle
	 */
	public float getQuality() {
		return quality;
	}

	/**
	 * A puzzle can have a model even if puzzleModel is null. This is because the model might not have
	 * been retrieved from the datastore.
	 * 
	 * @return True if this puzzle has an attached model in the datastore
	 */
	public boolean hasModelInDatastore() {
		return modelInDatastore;	
	}
	
	/**
	 * @return The attached puzzle model
	 */
	public PuzzleModel getPuzzleModel() {
		return puzzleModel;
	}

	/**
	 * @return The HistoryInfoKey of the history attached to this tutorial (null if it's not a tutorial)
	 */
	public String getTutorialHistoryKey() {
		return tutorialHistoryKey ;
	}
	
	
	


	/**
	 * A specific user needs to be attached to this puzzle info structure in order for the following fields
	 * to make sense. The methods:
	 *   getSolvedByUserHistoryKey
	 *   getStartedByUserHistoryKey
	 *   isSolvedByUser
	 *   isStartedByUser
	 * Make reference to the specific user that can be obtained via this method
	 * 
	 * @return The email of the user specifically attached to this puzzle info
	 */
	public String getSpecificUserEmail() {
		return specificUserEmail;
	}
	
	
	/**
	 * @return The history hey of the last solve of this puzzle by the specific user, null if none
	 */
	public String getSolvedByUserHistoryKey() {
		return solvedByUserHistoryKey;
	}
	
	/**
	 * @return The history hey of the last incomplete solve of this puzzle by the specific user, null if none
	 */
	public String getStartedByUserHistoryKey() {
		return startedByUserHistoryKey;
	}
	
	/**
	 * @return True if this puzzle has been solved by the specific user, false otherwise
	 */
	public boolean isSolvedByUser() {
		return solvedByUserHistoryKey != null;
	}	

	/**
	 * @return True if the specific user has at least one partial solve of this puzzle, false otherwise
	 */
	public boolean isStartedByUser() {
		return startedByUserHistoryKey != null;
	}	
	
	
	
	/**
	 * @param key The key for this puzzle info object
	 */
	public void setKey(String key) {
		this.key = key;
	}

	/**
	 * @param authorUsername The author to set for that puzzle
	 */
	public void setAuthorUsername(String authorUsername) {
		this.authorUsername = authorUsername;
	}

	/**
	 * @param dateCreated The creation date for that puzzle
	 */
	public void setDateCreated(Date dateCreated) {
		this.dateCreated  = dateCreated;
	}
	
	/**
	 * @param dateEdited The last edition date for that puzzle
	 */
	public void setDateEdited(Date dateEdited) {
		this.dateEdited  = dateEdited;
	}

	/**
	 * @param datePublished The publication date for that puzzle
	 */
	public void setDatePublished(Date datePublished) {
		this.datePublished  = datePublished;
	}

	/**
	 * @return The publication status of this puzzle
	 */
	public void setStatus(Status status) {
		this.status = status;
	}
	
	/**
	 * @param validity The validity status of this puzzle
	 */
	public void setValidity(Validity validity) {
		this.validity = validity;
	}

	/**
	 * @param difficulty The difficulty of this puzzle
	 */
	public void setDifficulty(float difficulty) {
		this.difficulty = difficulty;
	}

	/**
	 * @param quality The quality of this puzzle
	 */
	public void setQuality(float quality) {
		this.quality = quality;
	}

	/**
	 * A puzzle can have a model even if puzzleModel is null. This is because the model might not have
	 * been retrieved from the datastore.
	 * 
	 * @param modelInDatastore True to indicate that this puzzle has a model
	 */
	public void setHasModelInDatastore(boolean modelInDatastore) {
		this.modelInDatastore = modelInDatastore;	
	}

	/**
	 * @param puzzleModel The puzzle model to attach to this description
	 */
	public void setPuzzleModel(PuzzleModel puzzleModel) {
		this.puzzleModel = puzzleModel;
	}

	/**
	 * @param tutorialHistoryKey The key of the tutorial history attached to this object (null if its not a tutorial)
	 */
	public void setTutorialHistoryKey(String tutorialHistoryKey) {
		this.tutorialHistoryKey = tutorialHistoryKey;
	}

	
	
	
	/**
	 * A specific user needs to be attached to this puzzle info structure in order for the following fields
	 * to make sense. This method makes it possible to attach a specific user.
	 * 
	 * @param specificUserEmail The email of the user specifically attached to this puzzle info
	 */
	public void setSpecificUserEmail(String specificUserEmail) {
		this.specificUserEmail = specificUserEmail;
	}	

	/**
	 * @param solvedByUserHistoryKey The history hey of the last solve of this puzzle by the specific user, null if none
	 */
	public void setSolvedByUserHistoryKey(String solvedByUserHistoryKey) {
		this.solvedByUserHistoryKey = solvedByUserHistoryKey;
	}	

	/**
	 * @param startedByUserHistoryKey The history hey of the last incomplete solve of this puzzle by the specific user, null if none
	 */
	public void setStartedByUserHistoryKey(String startedByUserHistoryKey) {
		this.startedByUserHistoryKey = startedByUserHistoryKey;
	}	
	
	
	
	/**
	 * Formats the details (title, publication style, etc.) of the puzzle in a string
	 * 
	 * @param useHTML True to generate HTML markup in the string
	 * @return The formatted string
	 */
	public String getDetails(boolean useHTML) {

		long currTime = System.currentTimeMillis(); 
		
		String result =  TextUtil.putInSpan(getTitle(), "puzzle_title", useHTML);

		result += " by " + getAuthorUsername();

		if( !isPublished() ) {
			result += TextUtil.putInSpan(
					" created " + DateUtil.relativeDate( dateCreated, currTime ), "puzzle_date", useHTML );
			result += TextUtil.putInSpan(
					" edited " + DateUtil.relativeDate( dateEdited, currTime ), "puzzle_date", useHTML );
		}
		else {
			if( datePublished == null )
				result += TextUtil.putInSpan( " not yet published", "puzzle_date", useHTML );
			else
				result += TextUtil.putInSpan(
						" published " + DateUtil.relativeDate( datePublished, currTime ), "puzzle_date", useHTML );
		}
		
		
		return result;
	}

	/**
	 * @return True if the puzzle is in a published state, false otherwise
	 */
	public boolean isPublished() {
		return (status == Status.PUBLISHED || status == Status.REVIEW_PUBLISHED);
	}

	/**
	 * @return True if the puzzle is in the review process 
	 */
	public boolean isInReviewProcess() {
		return (status == Status.IN_REVIEW || status == Status.ACCEPTED);
	}

	/**
	 * @return True if the has been reviewed and accepted 
	 */
	public boolean isAccepted() {
		return status == Status.ACCEPTED;
	}

	/**
	 * True if the current puzzle comes after the passed puzzle in the standard date ordering:
	 *   datePublished,  dateEdited,  dateCreated
	 * NULL dates are considered 00/00/0000
	 * 
	 * @param other The object to compare to
	 * @return A negative value if the data of this object comes before the date of the other. 
	 *         A positive value if the date of this object comes after the date of the other. 
	 *         0 if the dates are equal.
	 */
	public int compareTo(PuzzleInfo other) {
		int result = DateUtil.compareDate( datePublished, other.getDatePublished() );
		if( result == 0 )
			result = DateUtil.compareDate( dateEdited, other.getDateEdited() );
		if( result == 0 )
			result = DateUtil.compareDate( dateCreated , other.getDateCreated() );
		if( result == 0 )
			result = key.compareTo( other.key );
		return result;
	}


}